#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
批量操作模块 - 负责批量重命名、格式转换、压缩解压缩等功能

这个模块提供了对文件的批量操作功能，包括：
1. 批量重命名文件（普通替换或正则表达式替换）
2. 按序列批量重命名文件（例如：img_001.jpg, img_002.jpg...）
3. 压缩文件和解压缩文件
4. 批量压缩/解压缩操作
5. 添加/移除图片水印（需要安装Pillow库）

使用示例：

1. 批量重命名文件(将所有文件中的"old"替换为"new")：
   ```python
   from core import batch
   
   # 预览模式，不实际重命名
   results = batch.batch_rename("/path/to/files", "old", "new", preview=True)
   for old_path, new_path in results.items():
       print(f"将重命名: {old_path} -> {new_path}")
   
   # 实际执行重命名
   batch.batch_rename("/path/to/files", "old", "new")
   ```

2. 按序列批量重命名（img_001, img_002...）：
   ```python
   # 将目录中的所有文件按"photo_{}"格式重命名
   batch.batch_rename_sequence("/path/to/photos", "photo_{}", start_number=1, padding=3)
   ```

3. 压缩文件：
   ```python
   # 将多个文件压缩到一个zip文件
   files = ["/path/file1.txt", "/path/file2.jpg"]
   batch.compress_files(files, "/path/output.zip", format="zip")
   ```

4. 解压缩文件：
   ```python
   # 解压缩一个文件到指定目录
   batch.extract_archive("/path/archive.zip", "/path/output_dir")
   ```

5. 批量压缩操作：
   ```python
   # 批量压缩目录中的所有jpg文件，每个文件单独压缩
   batch.batch_compress("/path/photos", "*.jpg", individual=True)
   ```

6. 批量解压操作：
   ```python
   # 解压目录中的所有zip文件
   batch.batch_extract("/path/archives", "*.zip", "/path/output")
   ```

7. 添加水印（需要Pillow库）：
   ```python
   # 安装Pillow: pip install Pillow
   # 为图片添加水印
   batch.add_watermark("/path/image.jpg", "Copyright 2023", position="bottom-right")
   
   # 批量添加水印
   batch.batch_watermark("/path/photos", "Copyright 2023", pattern="*.jpg")
   ```
"""
import os
import re
import shutil
import zipfile
import tarfile
from pathlib import Path
from typing import List, Dict, Union, Optional, Callable, Pattern, Tuple
import glob


def batch_rename(directory: Union[str, Path], pattern: str, replacement: str,
                use_regex: bool = False, recursive: bool = False,
                preview: bool = False) -> Dict[str, str]:
    """
    批量重命名文件
    
    Args:
        directory: 要处理的目录
        pattern: 要查找的模式（可以是文本或正则表达式）
        replacement: 替换文本
        use_regex: 是否使用正则表达式
        recursive: 是否递归处理子目录
        preview: 是否只预览而不实际重命名
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为新路径
    
    使用示例:
        # 简单替换：将所有文件名中的"old"替换为"new"
        batch_rename("/path/to/dir", "old", "new")
        
        # 使用正则表达式：将所有数字替换为"X"
        batch_rename("/path/to/dir", r"\d+", "X", use_regex=True)
        
        # 在递归模式下预览
        result = batch_rename("/path/to/dir", "old", "new", recursive=True, preview=True)
        print(result)  # 显示将进行的重命名操作
    """
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 编译正则表达式（如果需要）
    pattern_re = re.compile(pattern) if use_regex else None
    
    # 查找所有文件
    if recursive:
        files = list(directory_path.rglob("*"))
    else:
        files = list(directory_path.iterdir())
    
    # 只处理文件（不处理目录）
    files = [f for f in files if f.is_file()]
    
    rename_map = {}
    
    for file_path in files:
        old_name = file_path.name
        new_name = old_name
        
        # 应用重命名规则
        if use_regex:
            new_name = pattern_re.sub(replacement, old_name)
        else:
            new_name = old_name.replace(pattern, replacement)
        
        # 如果名称确实改变了
        if new_name != old_name:
            new_path = file_path.parent / new_name
            rename_map[str(file_path)] = str(new_path)
            
            # 如果不是预览模式，执行重命名
            if not preview:
                # 检查是否会覆盖现有文件
                if new_path.exists():
                    # 可以实现更复杂的冲突解决策略，这里简单地添加后缀
                    name, ext = os.path.splitext(new_name)
                    new_name = f"{name}_copy{ext}"
                    new_path = file_path.parent / new_name
                    rename_map[str(file_path)] = str(new_path)
                
                try:
                    file_path.rename(new_path)
                except Exception as e:
                    print(f"重命名文件 {file_path} 时出错: {e}")
    
    return rename_map


def batch_rename_sequence(directory: Union[str, Path], pattern: str, 
                        start_number: int = 1, padding: int = 3,
                        recursive: bool = False, preview: bool = False) -> Dict[str, str]:
    """
    按序列批量重命名文件
    
    Args:
        directory: 要处理的目录
        pattern: 命名模式，使用 {} 作为数字的占位符
        start_number: 起始编号
        padding: 数字的填充长度
        recursive: 是否递归处理子目录
        preview: 是否只预览而不实际重命名
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为新路径
    
    使用示例:
        # 将目录中的所有图片按顺序重命名为photo_001.jpg, photo_002.jpg...
        batch_rename_sequence("/path/to/photos", "photo_{}", start_number=1, padding=3)
        
        # 预览重命名操作，不实际执行
        result = batch_rename_sequence("/path/to/photos", "vacation_{}", preview=True)
        print(result)  # 显示将进行的重命名操作
    """
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 查找所有文件
    if recursive:
        files = list(directory_path.rglob("*"))
    else:
        files = list(directory_path.iterdir())
    
    # 只处理文件（不处理目录）并排序
    files = sorted([f for f in files if f.is_file()], key=lambda f: f.name)
    
    rename_map = {}
    current_number = start_number
    
    for file_path in files:
        # 获取文件扩展名
        ext = file_path.suffix
        
        # 生成新名称
        number_str = str(current_number).zfill(padding)
        new_name = pattern.format(number_str) + ext
        new_path = file_path.parent / new_name
        
        rename_map[str(file_path)] = str(new_path)
        
        # 如果不是预览模式，执行重命名
        if not preview:
            # 检查是否会覆盖现有文件
            if new_path.exists():
                # 简单地添加后缀
                name, ext = os.path.splitext(new_name)
                new_name = f"{name}_copy{ext}"
                new_path = file_path.parent / new_name
                rename_map[str(file_path)] = str(new_path)
            
            try:
                file_path.rename(new_path)
            except Exception as e:
                print(f"重命名文件 {file_path} 时出错: {e}")
        
        current_number += 1
    
    return rename_map


def compress_files(files: List[Union[str, Path]], output_file: Union[str, Path],
                 format: str = 'zip', compression_level: int = 9) -> bool:
    """
    压缩文件
    
    Args:
        files: 要压缩的文件列表
        output_file: 输出的压缩文件路径
        format: 压缩格式 ('zip', 'tar', 'gztar', 'bztar', 'xztar')
        compression_level: 压缩级别 (0-9)
        
    Returns:
        bool: 操作是否成功
    
    使用示例:
        # 压缩多个文件到一个zip文件
        files = ["/path/to/file1.txt", "/path/to/file2.jpg"]
        compress_files(files, "/path/to/output.zip")
        
        # 使用不同的压缩格式
        compress_files(files, "/path/to/output.tar.gz", format="gztar")
    """
    output_path = Path(output_file)
    
    # 确保输出目录存在
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    # 检查文件列表是否为空
    if not files:
        raise ValueError("文件列表不能为空")
    
    # 检查所有文件是否存在
    for file_path in files:
        path = Path(file_path)
        if not path.exists():
            raise ValueError(f"文件不存在: {file_path}")
    
    # 根据格式选择压缩方法
    try:
        if format == 'zip':
            with zipfile.ZipFile(output_path, 'w', compression=zipfile.ZIP_DEFLATED, 
                               compresslevel=compression_level) as zipf:
                for file_path in files:
                    path = Path(file_path)
                    if path.is_dir():
                        for root, _, files_in_dir in os.walk(path):
                            for file in files_in_dir:
                                file_full_path = os.path.join(root, file)
                                zipf.write(file_full_path, os.path.relpath(file_full_path, start=os.path.commonpath([path.parent])))
                    else:
                        zipf.write(path, path.name)
        elif format in ['tar', 'gztar', 'bztar', 'xztar']:
            mode = ''
            if format == 'tar':
                mode = 'w'
            elif format == 'gztar':
                mode = 'w:gz'
            elif format == 'bztar':
                mode = 'w:bz2'
            elif format == 'xztar':
                mode = 'w:xz'
                
            with tarfile.open(output_path, mode) as tar:
                for file_path in files:
                    path = Path(file_path)
                    tar.add(path, arcname=path.name)
        else:
            raise ValueError(f"不支持的压缩格式: {format}")
            
        return True
    except Exception as e:
        print(f"压缩文件时出错: {e}")
        return False


def extract_archive(archive_file: Union[str, Path], output_dir: Union[str, Path],
                  password: Optional[str] = None) -> bool:
    """
    解压缩文件
    
    Args:
        archive_file: 压缩文件路径
        output_dir: 输出目录
        password: 压缩文件密码（如有）
        
    Returns:
        bool: 操作是否成功
    
    使用示例:
        # 解压缩zip文件
        extract_archive("/path/to/archive.zip", "/path/to/output_dir")
        
        # 解压缩带密码的zip文件
        extract_archive("/path/to/protected.zip", "/path/to/output_dir", password="secret")
    """
    archive_path = Path(archive_file)
    output_path = Path(output_dir)
    
    # 检查压缩文件是否存在
    if not archive_path.exists():
        raise ValueError(f"压缩文件不存在: {archive_file}")
    
    # 确保输出目录存在
    output_path.mkdir(parents=True, exist_ok=True)
    
    try:
        # 尝试确定文件类型并解压
        if archive_path.suffix.lower() in ['.zip', '.jar', '.apk']:
            with zipfile.ZipFile(archive_path, 'r') as zipf:
                if password:
                    zipf.setpassword(password.encode())
                zipf.extractall(output_path)
        elif archive_path.suffix.lower() in ['.tar', '.gz', '.bz2', '.xz', '.tgz', '.tbz2', '.txz']:
            with tarfile.open(archive_path, 'r:*') as tar:
                tar.extractall(path=output_path)
        else:
            # 尝试用zipfile打开未知格式
            try:
                with zipfile.ZipFile(archive_path, 'r') as zipf:
                    if password:
                        zipf.setpassword(password.encode())
                    zipf.extractall(output_path)
            except zipfile.BadZipFile:
                # 如果不是zip，尝试用tarfile
                try:
                    with tarfile.open(archive_path, 'r:*') as tar:
                        tar.extractall(path=output_path)
                except tarfile.ReadError:
                    raise ValueError(f"不支持的压缩文件格式: {archive_path}")
        
        return True
    except Exception as e:
        print(f"解压缩文件时出错: {e}")
        return False


def batch_compress(directory: Union[str, Path], pattern: str = "*", 
                 recursive: bool = False, format: str = 'zip',
                 output_dir: Optional[Union[str, Path]] = None,
                 individual: bool = False) -> Dict[str, str]:
    """
    批量压缩文件或目录
    
    Args:
        directory: 要处理的目录
        pattern: 文件匹配模式
        recursive: 是否递归处理子目录
        format: 压缩格式 ('zip', 'tar', 'gztar', 'bztar', 'xztar')
        output_dir: 输出目录，默认为原目录
        individual: 是否单独压缩每个文件/目录
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为压缩文件路径
    
    使用示例:
        # 将目录中所有文件压缩到一个文件
        batch_compress("/path/to/files", pattern="*", individual=False)
        
        # 单独压缩目录中的每个jpg文件
        batch_compress("/path/to/photos", pattern="*.jpg", individual=True)
        
        # 递归压缩所有子目录中的文档文件到指定目录
        batch_compress("/path/to/docs", pattern="*.pdf", recursive=True, 
                      output_dir="/path/to/output", individual=True)
    """
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的文件
    if recursive:
        matched_items = list(directory_path.rglob(pattern))
    else:
        matched_items = list(directory_path.glob(pattern))
    
    # 根据individual参数决定压缩方式
    compression_map = {}
    
    if individual:
        # 单独压缩每个文件/目录
        for item in matched_items:
            if item.is_file() or item.is_dir():
                output_file = output_path / f"{item.name}.{format}"
                
                # 避免覆盖现有文件
                counter = 1
                while output_file.exists():
                    output_file = output_path / f"{item.stem}_{counter}.{format}"
                    counter += 1
                
                success = compress_files([item], output_file, format)
                if success:
                    compression_map[str(item)] = str(output_file)
    else:
        # 一次性压缩所有匹配的文件/目录
        if matched_items:
            output_file = output_path / f"{directory_path.name}.{format}"
            
            # 避免覆盖现有文件
            counter = 1
            while output_file.exists():
                output_file = output_path / f"{directory_path.name}_{counter}.{format}"
                counter += 1
            
            success = compress_files(matched_items, output_file, format)
            if success:
                compression_map[str(directory_path)] = str(output_file)
    
    return compression_map


def batch_extract(directory: Union[str, Path], pattern: str = "*.zip",
                output_dir: Optional[Union[str, Path]] = None,
                password: Optional[str] = None) -> Dict[str, str]:
    """
    批量解压缩文件
    
    Args:
        directory: 包含压缩文件的目录
        pattern: 文件匹配模式
        output_dir: 输出目录，默认为原压缩文件所在目录
        password: 压缩文件密码（如有）
        
    Returns:
        Dict[str, str]: 映射，键为压缩文件路径，值为解压目录路径
    
    使用示例:
        # 解压目录中的所有zip文件
        batch_extract("/path/to/archives", "*.zip")
        
        # 解压所有压缩文件到指定目录
        batch_extract("/path/to/archives", pattern="*.zip", 
                    output_dir="/path/to/output")
    """
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 设置默认输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的压缩文件
    matched_archives = list(directory_path.glob(pattern))
    
    extraction_map = {}
    
    for archive in matched_archives:
        if archive.is_file():
            # 为每个压缩文件创建单独的输出目录
            archive_output = output_path / archive.stem
            
            # 避免目录名冲突
            counter = 1
            while archive_output.exists():
                archive_output = output_path / f"{archive.stem}_{counter}"
                counter += 1
            
            archive_output.mkdir(exist_ok=True)
            
            success = extract_archive(archive, archive_output, password)
            if success:
                extraction_map[str(archive)] = str(archive_output)
    
    return extraction_map


# 批量图片处理需要额外的依赖，使用时请先安装 Pillow 库
try:
    from PIL import Image, ImageDraw, ImageFont
    _has_pillow = True
except ImportError:
    _has_pillow = False


def add_watermark(image_file: Union[str, Path], watermark_text: str,
                output_file: Optional[Union[str, Path]] = None,
                position: str = "center", opacity: float = 0.5,
                font_size: int = 30, color: Tuple[int, int, int] = (255, 255, 255)) -> bool:
    """
    为图片添加水印
    
    Args:
        image_file: 图片文件路径
        watermark_text: 水印文本
        output_file: 输出文件路径，默认覆盖原文件
        position: 水印位置 ("center", "top-left", "top-right", "bottom-left", "bottom-right")
        opacity: 不透明度 (0.0-1.0)
        font_size: 字体大小
        color: RGB颜色元组
        
    Returns:
        bool: 操作是否成功
    
    使用示例:
        # 安装依赖库：pip install Pillow
        
        # 添加居中水印
        add_watermark("/path/to/image.jpg", "Copyright 2023")
        
        # 添加自定义位置和样式的水印
        add_watermark("/path/to/image.jpg", "Company Name", 
                     position="bottom-right", opacity=0.7, 
                     font_size=24, color=(255, 0, 0))
    """
    if not _has_pillow:
        raise ImportError("此功能需要安装 Pillow 库: pip install Pillow")
    
    image_path = Path(image_file)
    if not image_path.exists():
        raise ValueError(f"图片文件不存在: {image_file}")
    
    # 设置默认输出文件
    if output_file is None:
        output_path = image_path
    else:
        output_path = Path(output_file)
    
    try:
        # 打开原图
        image = Image.open(image_path)
        
        # 创建透明图层作为水印
        watermark = Image.new('RGBA', image.size, (0, 0, 0, 0))
        draw = ImageDraw.Draw(watermark)
        
        # 设置字体
        try:
            font = ImageFont.truetype("arial.ttf", font_size)
        except IOError:
            font = ImageFont.load_default()
        
        # 获取文本大小
        text_width, text_height = draw.textsize(watermark_text, font=font)
        
        # 确定位置
        if position == "center":
            position = ((image.width - text_width) // 2, (image.height - text_height) // 2)
        elif position == "top-left":
            position = (10, 10)
        elif position == "top-right":
            position = (image.width - text_width - 10, 10)
        elif position == "bottom-left":
            position = (10, image.height - text_height - 10)
        elif position == "bottom-right":
            position = (image.width - text_width - 10, image.height - text_height - 10)
        else:
            position = ((image.width - text_width) // 2, (image.height - text_height) // 2)
        
        # 绘制水印文本
        draw.text(position, watermark_text, font=font, fill=(*color, int(255 * opacity)))
        
        # 混合水印和原图
        if image.mode != 'RGBA':
            image = image.convert('RGBA')
        
        watermarked = Image.alpha_composite(image, watermark)
        
        # 保存图片
        watermarked = watermarked.convert('RGB')  # 移除透明通道
        watermarked.save(output_path)
        
        return True
    except Exception as e:
        print(f"添加水印时出错: {e}")
        return False


def remove_watermark(image_file: Union[str, Path], output_file: Optional[Union[str, Path]] = None,
                   tolerance: int = 30, bg_color: Optional[Tuple[int, int, int]] = None) -> bool:
    """
    尝试移除图片上的水印（简单方法，不能处理所有情况）
    
    Args:
        image_file: 图片文件路径
        output_file: 输出文件路径，默认覆盖原文件
        tolerance: 颜色差异容差
        bg_color: 背景颜色（如果已知）
        
    Returns:
        bool: 操作是否成功
    
    使用示例:
        # 安装依赖库：pip install Pillow
        
        # 尝试移除水印
        remove_watermark("/path/to/image.jpg")
        
        # 自定义参数移除水印
        remove_watermark("/path/to/image.jpg", tolerance=50, 
                        bg_color=(255, 255, 255))
                        
    注意:
        这个函数使用简单的颜色差异算法，只能处理简单的情况。
        复杂的水印可能需要更高级的图像处理技术。
    """
    if not _has_pillow:
        raise ImportError("此功能需要安装 Pillow 库: pip install Pillow")
    
    image_path = Path(image_file)
    if not image_path.exists():
        raise ValueError(f"图片文件不存在: {image_file}")
    
    # 设置默认输出文件
    if output_file is None:
        output_path = image_path
    else:
        output_path = Path(output_file)
    
    try:
        # 打开原图
        image = Image.open(image_path)
        
        # 转换为RGB模式
        if image.mode != 'RGB':
            image = image.convert('RGB')
        
        # 获取图像数据
        pixels = image.load()
        width, height = image.size
        
        # 简单的水印移除：寻找与背景色相近的像素并替换
        # 这个方法只适用于简单的水印，更复杂的水印需要更高级的图像处理技术
        
        if bg_color is None:
            # 假设图像四角的颜色是背景色
            corners = [
                pixels[0, 0],
                pixels[width-1, 0],
                pixels[0, height-1],
                pixels[width-1, height-1]
            ]
            
            # 取颜色均值作为背景色
            bg_red = sum(c[0] for c in corners) // 4
            bg_green = sum(c[1] for c in corners) // 4
            bg_blue = sum(c[2] for c in corners) // 4
            
            bg_color = (bg_red, bg_green, bg_blue)
        
        # 处理每个像素
        for y in range(height):
            for x in range(width):
                r, g, b = pixels[x, y]
                
                # 计算与背景色的差异
                diff = (abs(r - bg_color[0]) + abs(g - bg_color[1]) + abs(b - bg_color[2])) // 3
                
                # 如果差异很小，认为是水印
                if diff < tolerance:
                    pixels[x, y] = bg_color
        
        # 保存图片
        image.save(output_path)
        
        return True
    except Exception as e:
        print(f"移除水印时出错: {e}")
        return False


def batch_watermark(directory: Union[str, Path], watermark_text: str,
                  pattern: str = "*.jpg",
                  recursive: bool = False,
                  output_dir: Optional[Union[str, Path]] = None,
                  **kwargs) -> Dict[str, str]:
    """
    批量为图片添加水印
    
    Args:
        directory: 要处理的目录
        watermark_text: 水印文本
        pattern: 文件匹配模式
        recursive: 是否递归处理子目录
        output_dir: 输出目录，默认为原目录
        **kwargs: 传递给add_watermark的额外参数
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为处理后的路径
    
    使用示例:
        # 安装依赖库：pip install Pillow
        
        # 为目录中所有jpg图片添加水印
        batch_watermark("/path/to/photos", "Copyright 2023")
        
        # 递归处理所有子目录中的png图片，并输出到指定目录
        batch_watermark("/path/to/photos", "Company Logo", pattern="*.png", 
                      recursive=True, output_dir="/path/to/output",
                      position="bottom-right", opacity=0.8)
    """
    if not _has_pillow:
        raise ImportError("此功能需要安装 Pillow 库: pip install Pillow")
    
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的图片文件
    if recursive:
        matched_images = list(directory_path.rglob(pattern))
    else:
        matched_images = list(directory_path.glob(pattern))
    
    processed_map = {}
    
    for image_file in matched_images:
        if image_file.is_file():
            # 设置输出文件路径
            if output_path == directory_path:
                # 覆盖原文件
                output_file = image_file
            else:
                # 在输出目录创建相同的目录结构
                rel_path = image_file.relative_to(directory_path)
                output_file = output_path / rel_path
                
                # 确保父目录存在
                output_file.parent.mkdir(parents=True, exist_ok=True)
            
            success = add_watermark(image_file, watermark_text, output_file, **kwargs)
            if success:
                processed_map[str(image_file)] = str(output_file)
    
    return processed_map


def batch_remove_watermark(directory: Union[str, Path], pattern: str = "*.jpg",
                         recursive: bool = False,
                         output_dir: Optional[Union[str, Path]] = None,
                         **kwargs) -> Dict[str, str]:
    """
    批量移除图片上的水印
    
    Args:
        directory: 要处理的目录
        pattern: 文件匹配模式
        recursive: 是否递归处理子目录
        output_dir: 输出目录，默认为原目录
        **kwargs: 传递给remove_watermark的额外参数
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为处理后的路径
    
    使用示例:
        # 安装依赖库：pip install Pillow
        
        # 批量移除目录中所有jpg图片的水印
        batch_remove_watermark("/path/to/photos")
        
        # 使用自定义参数批量处理
        batch_remove_watermark("/path/to/photos", pattern="*.png", 
                             recursive=True, output_dir="/path/to/output",
                             tolerance=40, bg_color=(255, 255, 255))
    """
    if not _has_pillow:
        raise ImportError("此功能需要安装 Pillow 库: pip install Pillow")
    
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的图片文件
    if recursive:
        matched_images = list(directory_path.rglob(pattern))
    else:
        matched_images = list(directory_path.glob(pattern))
    
    processed_map = {}
    
    for image_file in matched_images:
        if image_file.is_file():
            # 设置输出文件路径
            if output_path == directory_path:
                # 覆盖原文件
                output_file = image_file
            else:
                # 在输出目录创建相同的目录结构
                rel_path = image_file.relative_to(directory_path)
                output_file = output_path / rel_path
                
                # 确保父目录存在
                output_file.parent.mkdir(parents=True, exist_ok=True)
            
            success = remove_watermark(image_file, output_file, **kwargs)
            if success:
                processed_map[str(image_file)] = str(output_file)
    
    return processed_map 